package tools

import (
	"com.lh.basic.service/lib/crypto"
	"com.lh.basic.service/pb/auth"
	"com.lh.data.data/lib/redis"
	"context"
	"crypto/sha256"
	"encoding/hex"
	"errors"
	"fmt"
	"github.com/golang-jwt/jwt/v5"
	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/metadata"
	"google.golang.org/grpc/status"
	"net/http"
	"regexp"
	"time"
)

type CustomClaims struct {
	UserId   string `json:"user_id"`
	Username string `json:"username"`
	jwt.RegisteredClaims
}

// 生成token
func GetToken(infos *auth.GTokenConfig) (*string, error) {
	if len(infos.Sid) > 0 {
		cry := crypto.Init()
		pw, err := cry.PWEncode(infos.Sid, cry.Id.String())
		if err != nil {
			return nil, err
		}
		newPw := *pw
		duration := time.Hour * 24
		if infos.Duration.IsValid() {
			duration = infos.Duration.AsDuration()
		}
		now := time.Now()
		claims := CustomClaims{
			UserId:   infos.Userid,
			Username: infos.Username,
			RegisteredClaims: jwt.RegisteredClaims{
				Issuer:    "allkic",                              // 签名者
				Subject:   infos.Sid,                             // 主题
				ID:        cry.Id.String(),                       // 签名id
				ExpiresAt: jwt.NewNumericDate(now.Add(duration)), // 过期时间
				NotBefore: jwt.NewNumericDate(now),
				IssuedAt:  jwt.NewNumericDate(now), // 签发时间
			},
		}
		token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
		privateKey := []byte(fmt.Sprintf("%s-%s", newPw, cry.Id.String()))
		hash := sha256.Sum256(privateKey)
		hexStr := hex.EncodeToString(hash[:])
		signToken, err := token.SignedString([]byte(hexStr))
		if err != nil {
			return nil, err
		}
		red := redis.Init()
		if err := red.SetHRow(redis.HConfig{
			Duration: duration,
			Key:      fmt.Sprintf("token_%s:1", infos.Sid),
			HList:    []interface{}{"uuid", cry.Id.String(), "key", hexStr},
		}); err != nil {
			return nil, err
		}
		return &signToken, nil
	}
	return nil, errors.New("请刷新浏览器，如问题未解决请联系我们")
}

// 验证token
func ValidateToken(c context.Context) bool {
	md, ok := metadata.FromIncomingContext(c)
	if !ok {
		return false
	}
	sid := md.Get("sid")
	token := md.Get("token")
	if len(sid) > 0 && len(token) > 0 {
		red := redis.Init()
		val, err := red.GetHRows(fmt.Sprintf("token_%s:1", sid[0]), "sid", "key")
		if !IsNil(val[0]) || !IsNil(val[1]) || err != nil {
			return false
		}
		tokens, err := jwt.ParseWithClaims(token[0], &CustomClaims{}, func(t *jwt.Token) (interface{}, error) {
			if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok {
				return nil, fmt.Errorf("unexpected signing method: %v", t.Header["alg"])
			}
			return val[1].(string), nil
		})
		if err != nil {
			return false
		}
		if claims, ok := tokens.Claims.(*CustomClaims); ok && tokens.Valid {
			if claims.Subject == sid[0] && claims.ID == val[0].(string) {
				return true
			} else {
				return false
			}
		}
	}
	return false
}

// grpc token验证
func AuthInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
	if ValidateToken(ctx) {
		newCtx := context.WithValue(ctx, "isLogin", true)
		return handler(newCtx, req)
	} else {
		return nil, status.Errorf(codes.Unauthenticated, "token is invalid")
	}
}

// grpc 过滤特定条件下 token验证
func AuthFilterInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
	reg := regexp.MustCompile(`^(/auth.)`)
	if reg.MatchString(info.FullMethod) {
		return handler(ctx, req)
	}
	if ValidateToken(ctx) {
		newCtx := context.WithValue(ctx, "isLogin", true)
		return handler(newCtx, req)
	} else {
		return nil, status.Errorf(codes.Unauthenticated, "token is invalid")
	}
}

// http 特定条件下 token 验证
func AuthHttpInterceptor(r *http.Request) bool {
	if ValidateToken(r.Context()) {
		return true
	} else {
		return false
	}
}

// http token 验证
func AuthFilterHttpInterceptor(r *http.Request) bool {
	reg := regexp.MustCompile(`^(/v1/auth)`)
	if reg.MatchString(r.URL.String()) {
		return true
	}
	if ValidateToken(r.Context()) {
		return true
	} else {
		return false
	}
}
